{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import torch\n",
    "import networkx as nx\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Visualization function for NX graph or PyTorch tensor\n",
    "def visualize(h, color, epoch=None, loss=None):\n",
    "    plt.figure(figsize=(7,7))\n",
    "    plt.xticks([])\n",
    "    plt.yticks([])\n",
    "\n",
    "    if torch.is_tensor(h):  #可视化神经网络运行中间结果\n",
    "        h = h.detach().cpu().numpy()\n",
    "        plt.scatter(h[:, 0], h[:, 1], s=140, c=color, cmap=\"Set2\")\n",
    "        if epoch is not None and loss is not None:\n",
    "            plt.xlabel(f'Epoch: {epoch}, Loss: {loss.item():.4f}', fontsize=16)\n",
    "    else:  #可视化图\n",
    "        nx.draw_networkx(G, pos=nx.spring_layout(G, seed=42), with_labels=False,\n",
    "                         node_color=color, cmap=\"Set2\")\n",
    "    plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Downloading https://www.chrsmrrs.com/graphkerneldatasets/ENZYMES.zip\n",
      "Extracting \\tmp\\ENZYMES\\ENZYMES\\ENZYMES.zip\n",
      "Processing...\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ENZYMES(600)\n",
      "600\n",
      "6\n",
      "3\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Done!\n"
     ]
    }
   ],
   "source": [
    "from torch_geometric.datasets import TUDataset\n",
    "\n",
    "dataset = TUDataset(root=\"/tmp/ENZYMES\",name='ENZYMES')\n",
    "\n",
    "print(dataset)\n",
    "print(len(dataset))\n",
    "print(dataset.num_classes)\n",
    "print(dataset.num_node_features)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = dataset[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.is_undirected()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using existing file ind.cora.x\n",
      "Using existing file ind.cora.tx\n",
      "Using existing file ind.cora.allx\n",
      "Downloading https://github.com/kimiyoung/planetoid/raw/master/data/ind.cora.y\n",
      "Downloading https://github.com/kimiyoung/planetoid/raw/master/data/ind.cora.ty\n",
      "Downloading https://github.com/kimiyoung/planetoid/raw/master/data/ind.cora.ally\n",
      "Downloading https://github.com/kimiyoung/planetoid/raw/master/data/ind.cora.graph\n",
      "Downloading https://github.com/kimiyoung/planetoid/raw/master/data/ind.cora.test.index\n",
      "Processing...\n",
      "Done!\n"
     ]
    }
   ],
   "source": [
    "from torch_geometric.datasets import Planetoid\n",
    "\n",
    "dataset = Planetoid(root='/tmp/Cora', name='Cora')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "7\n",
      "1433\n"
     ]
    }
   ],
   "source": [
    "print(len(dataset))\n",
    "\n",
    "print(dataset.num_classes)\n",
    "\n",
    "print(dataset.num_node_features)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "140\n",
      "500\n",
      "1000\n"
     ]
    }
   ],
   "source": [
    "data = dataset[0]\n",
    "\n",
    "print(data.is_undirected())\n",
    "\n",
    "print(data.train_mask.sum().item())\n",
    "\n",
    "print(data.val_mask.sum().item())\n",
    "\n",
    "print(data.test_mask.sum().item())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dataset: KarateClub():\n",
      "======================\n",
      "Number of graphs: 1\n",
      "Number of features: 34\n",
      "Number of classes: 4\n"
     ]
    }
   ],
   "source": [
    "from torch_geometric.datasets import KarateClub\n",
    "\n",
    "dataset = KarateClub()\n",
    "print(f'Dataset: {dataset}:')\n",
    "print('======================')\n",
    "print(f'Number of graphs: {len(dataset)}')\n",
    "print(f'Number of features: {dataset.num_features}')\n",
    "print(f'Number of classes: {dataset.num_classes}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建Data\n",
    "\n",
    "import torch\n",
    "from torch_geometric.data import Data\n",
    "\n",
    "edge_index = torch.tensor([[0, 1, 1, 2],\n",
    "                           [1, 0, 2, 1]], dtype=torch.long)\n",
    "x = torch.tensor([[-1], [0], [1]], dtype=torch.float)\n",
    "\n",
    "data = Data(x=x, edge_index=edge_index)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAZQAAAGKCAYAAAArGbdLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAABJ8UlEQVR4nO3dd2AUdeI28GfK1hRCSEiA0ELvNRRFQERBioJgPyznWe7Uu3vVs576s5+ed57l7Hqih6cUESmC9CqQgPQivYQW0rN9Z+b9IwYpAXZ3ZrPt+fyZZIcnmuTZmW8TNE0DERGRXmKkAxARUXxgoRARkSFYKEREZAgWChERGYKFQkREhpAv9MmioiLtwIEDdZWFiIhiQO/evU8CyDz74xcslAMHDiAvLy9soYiIKPZomlbrnQYfeRERkSFYKEREZAgWChERGYKFQkREhmChEBGRIVgoRERkCBYKEREZgoVCRESGYKEQEZEhWChERGQIFgoRERmChUJERIZgoRARkSFYKEREZAgWChERGYKFQkREhmChEBGRIVgoRERkCBYKEREZgoVCRESGYKEQEZEhWChERGQIOdIBiIhCZbfbMWTIEFxxxRVITU2FKIpwOBxYvXo15syZg5KSkkhHTCgsFCKKOfXq1cOdd96JAQMGQNM0WK3WU5/LyMhAo0aNMHbsWGzevBmffvopDh48GMG0iYOPvIgopjRu3BhvvfUWBg0aBIvFckaZ1DCbzTCbzejRowdef/11dO3aNQJJEw8LhYhiRnp6Ol577TWkpaVBli/+gEUURdhsNjz99NNo06ZNHSRMbCwUIooZjzzyCJKSkiCKwf3pslqtePbZZ4N+HQWH/3WJKCZkZ2ejbdu2Ad2Z1MZkMqFPnz4Gp6LTsVCIKCaMHj1a1x2G3W7HuHHjDExEZ2OhEFFMGDp0KEwmk65r5ObmIj093aBEdDYWChFFPUmSap3NFSyfz8dCCSMWChFFPZPJBFVVdV9H0zSYzWYDElFtWChEFPXcbrchM7QEQYDD4TAgEdWGhUJEMaGwsFD3NSRJwrFjxwxIQ7VhoRBRTJg2bRpcLlfIr/f7/Vi8eDE8Ho+Bqeh0LBQiignLly/X9XpFUTBjxgyD0lBtWChEFBO8Xi+mTp0Kt9sd9Gs9Hg82bNhgyGMzOj8WChHFjMmTJ2PNmjVBlYrH48GRI0fw97//PYzJCGChEFGM+cc//oEFCxbA7XZDUZTzfp2qqnC5XNi5cyceffRRjp3UAZ6HQkQxRdM0fPDBB1iwYAGuvfZaXHLJJVAUBZIkQRAE+P1+yLKMrVu34ptvvsHGjRuhaVqkYycE4UL/oQsKCrS8vLw6jENEFJykpCT07Nnz1ImNVVVV2Lx5M06ePBnpaHFL07R1AHqf/XHeoRBRTHM4HLpngJExOIZCRESGYKEQEZEhWChERGQIFgoRERmChUJERIZgoRARkSFYKEREZAgWChERGYKFQkREhmChEBGRIVgoRERkCBYKEREZgoVCRESGYKEQEZEhWChERGQIFgoRERmChUJERIZgoRARkSFYKEREZAieKU8Uw9LS0jBs2DD06dMHycnJUBQFZWVlWLBgAVasWAGv1xvpiJRABE3TzvvJgoICLS8vrw7jEFEgcnJycPvtt6Nnz57QNA0Wi+WMzzudTgiCgPnz52PSpElwOp0RSkrxSNO0dQB6n/1xPvIiijFdunTBP//5T/Tp0wdms/mcMgEAu90Om82G4cOH480330RGRkYEklKiYaEQxZA2bdrgmWeegc1mgyhe/NfXbDYjIyMDr732GpKTk+sgISUyFgpRjJAkCc8++yysVmtQr5NlGWlpafjzn/8cnmBEv+CgPOmWndMKmdnNYLHY4fE4UXT0II4V7ol0rLjTr18/mEymkF5rMpnQo0cPpKeno6SkxOBkRNVYKBQS2WRGt7yhGDLydqRnNIKiKBAFEaqmQpIklBQdwaI5E7ExfyH8Ps40MsL48eNht9t1XWP48OH48ssvDUpEdCbO8qKgNWraBvf95d+QTWZYbUnn/Tq3ywGfz4P3X/sDjh3mHYsemZmZeO+992odgA9GWVkZJkyYYFAqSlSc5UWGaNqyIx586mPYk+tdsEwAwGpLQlJyGv7410+R07x9HSWMT5mZmfD5fLqvk5qaakAaotrxkRcFLDUtE/c88jYs1sAfu4iiCIvVjnsf/Tdee/IGVJYXhzFh/NJ7Z3I6SZKgKIph17uYzMxMDBw4EFlZWbBarSgvL8eOHTuwevXqOs1B4cdCoYANGnYLzObgZhjVMJmtGDjsFsye/LbBqRKDw+GAIAi6r6MoSp39Ee/evTvGjx+PDh06QBCEMyYUOJ1OPPDAA5gzZw5mzpyJsrKyOslE4cVHXhQQ2WRGv8FjIZvMIb3eZDKj/+DrIMmhzVJKdIWFhZBl/e//CgsLDUhzYYIg4N5778VTTz2Fbt26wWw2nzM7zW63Izk5GWPGjMF7772H1q1bhz0XhR8LhQLStfcVuq8hCAK69rrcgDSJx+Fw6H5E5HQ6MW3aNANT1e6BBx7A0KFDA1ovYzabkZycjJdffhktW7YMezYKLxYKBaR1+14XHYS/GKstCa06nDMxhAI0ffp03QPzK1euNChN7a666ioMHDgw6MWXFosFL774YtCvo+jCQqGAJKWmGXKd5JT6hlwnEe3Zswe7d+8OaQdht9uNqVOnGjJT7EJuvvnmkEpBFEWYTCYMGjQoDKmorrBQKCB+g7ZB93k9hlwnUb3wwgsoLi4Oqhjcbjfy8/MxZcqUMCYDunbtqmvhpc1mw/jx4w1MRHWNhUIBKSk+CkXx67qG4vejtPioQYkSk9PpxEMPPYQDBw5cdEt6RVHgdruxaNEivP7662HPNmbMGN2PrOrVq4e2bdsalIjqGqcNU0DWrZqDAVfcAEkK/UdGUfxYt+p7A1MlpqqqKjz88MPo168fxo0bh+bNm0PTNJhMJqiqCr/fD0mSkJ+fj+nTp2Pnzp11kqtZs2YB7YB8IZqmoUmTJvj5558NSkV1iYVCATl2eA+Kjh1Ak+btQr7G8SP7cPzIXgNTJS5VVbFq1SqsWrUKOTk56NKly6kTG8vLy5Gfn4+Kioo6zWTEgLokSUhK0jf5gyKHhUIBWzR7Im747V+DWilfw+N2YtGciWFIRYcPH8bhw4cjHQMej/7xMVVV4XK5DEhDkcAxFArYhrXzsXPLang9wf3Cez0u7Ni0CpvyF4YpGUWDI0eO6L6Gpmk4epTjbLGKhUJB+e/7f8WeHevhcQdWKh63C7u2F2DSB0+HORlF2nfffaf77Hqn04lt27YZlIjqGguFgqL4ffjkX/8Pi2Z/BpejEm6Xo9avc7sccDoqsXD2f/CfNx/WPUOMot+6det0rXNxu92YPn26gYmornEMhYKmaRoWzPwUi+ZMROeegzF4+K1o0DAHJrMVPq8bJ48fxtK5/8WWn5ZC5W6yCUNVVUydOhW33nprSAP0qqpi4UI+Fo1lLBQKmaoo2JS/kGMjdMqMGTPQqVMn9OjRI6gt991uN55//nk4HLXf8VJs4CMvIjKMpml49dVXkZ+fH9BsLUVR4HK58OKLL2Lr1q11kJDCiXcoRGQov9+PV199FYMGDcL111+PrKwsmEwmSJJ06mtcLhdEUcSyZcswZcoUzuyKEywUohgnyzK6deuGBg0awGQywel0YteuXRFfm7J06VIsXboUubm5GDp06KkTGysqKrB582YsXryYa07iDAuFKEZlZmZixIgRuPrqqyEIAkRRhCiKp7ZeOXDgAKZNm4Y1a9ZE9KjdvXv34sMPP4zYv091R9A07byfLCgo0PLy8uowDhEF4sorr8S9994LQRBgNp//FE2n04mSkhI89dRTKCkpqcOEFM80TVsH4JzDjTgoTxRjrrnmGtxzzz2wWCwXLBOg+qjd7Oxs/Otf/0J6enodJaRExUIhiiE9e/bEbbfdFtQ6D1mWkZKSgpdeeumMgXEio7FQiGLIb3/726DWd9SQZRnp6eno06dPGFIRVWOhEMWI3NxcZGVlhfx6u93OExEprDjLyyAWaxK69h6C+hnZsFiT4HSU4+ih3di+cSU0TY10PIoD1157LUwmk65rNG/eHDk5ORGfUkzxiYWiU1bjXAwafit69L0KmqbCZLZCFEWoigKv1wW/34/lP3yF1Uu+QVVlaaTjUgzr0KGD7jEQRVHQqlUrFgqFBQtFh0uGjMfoG/8ESZIgyWe+cxQlCVZbMgDgitF3YPDVt+LD1x/Ewb3cXoJCY7PZdF+DJyJSOHEMJUQDh92C0Tf+EWaL9ZwyOZvZbIXNnoL7HnsPzXI71VFCijd+v/4jADRNg9frNSAN0blYKCFo0yEPV1/3e5gtwb1jtFhsuOeRt5GUXC9MySieGbEwUVEULnCksOEjrxAMu+4+mC3Bn/cAAJIko++gMVg0m+erU3Bmz56NnJwc2O32kK+haRo2btxoYCr9GjVqhH79+iE9PR2SJKGsrAwbN27Ezp07Ix2NgsRCCVJGVjPkNG8b8uvNFhsGDrsFi+d8wdlfFJQVK1bgvvvuC/n1Xq8Xs2fPjui+XjUEQUDv3r0xfvx4tGrV6owtZBRFwfjx41FaWopp06ZhyZIlfEwXI/jIK0iXXnE9BFHfTBuTyYJ2nfsZlIgShdfrxQ8//ACPxxPS6zVNw/fff29wquDJsownn3wSf/nLX9CxY8dztpCRJAk2mw2NGzfG3XffjTfffBNpaWmRC0wBY6EEqVluJ8gXGYS/GNlkRqOcVgYlokTyxRdf4OjRo0Gf3e52u/Huu++iuLg4TMkCI4oi/u///g89evQIaNaa1WpFdnY23njjDaSmptZBQtKDhRIkm13/lEtZNsFqTzYgDSUaj8eDp556CoWFhQHfqXg8HkycOBGLFi0Kc7qLu/POO9GuXbugto+RZRn16tXDs88+G8ZkZAQWSpC8HrfuayiKHx43Dxai0FRUVODhhx/G999/D6fTCafTec7X+P1+uN1u7N27Fy+99BJmzZoVgaRnstlsuPrqq4Pa2LKGyWRC06ZN0aZNmzAkI6NwUD5IRccOoknzdhB1jKP4vB6UFvPIUwqd1+vFJ598gs8//xwDBgzAyJEjkZ6efurExm3btmHGjBnYv39/pKOeMnjwYKhq6BNRzGYzxo4di9dee83AVGQkFkqQVi6cgk49BsJiDX3qpiAI2LJ+iXGhKGH5fD4sXrwYixcvjnSUixo7dqyu1f6SJKFPnz5ISkqCw+EwMBkZhY+8grR/9yZUVYS+MMzv9yF/xSz4vKHN1CGKVXp2Sq7h9/uRnZ1tQBoKB96hhGDBzE8x5jd/gSXIlfIAoCoKls//Kgyp9GnSrC0GDL0RjZq2gcVqh9fjxLHDe7F8wdc4vH97pONRjLNYLLjQceOB0jRN18JOCi8WSgjWLp+Jtp36oVOPy4LafsXrcWHqxL/h5PFDYUwXnC69LsfwsfciPbMxJNkESfr1R6JR07bomjcEpcXH8MO3H2HD2vkRTEqxzOv1QhAE3dcRBAFut/6JMRQefOQVoi8/egZb1i8NeLaW1+PGjP/9C+tWzQlzssCNvulPuOWe55Cd0wpmi+2MMgGqn1mbLTZkNW6JG+56GmN/84ghfxQo8WiahvLyct3XMZlMKCoqMiARhQMLJUSqomDSB0/jmy9eRdGxg/C4XVDP2tLC7/PC5/Vg17Z8fPj6A1i95JsIpT3XiPH3o//gcQHfYVksNvS57BqMvunP4Q1GcWvOnDkhr/KvsWPHDpSVlRkTiAzHR146FaycjYKVs9EstxMuGTIeGVlNYbbY4HZW4sCeLVi1aCpKi49FOuYZWrXvhcuuvDHo3ZLNFhv6DRqDn7eswY7Nq8KUjuLV3Llzcf3114f8eqfTiWnTphmYiIzGQjHIwb1bY+bwrCtG3QnZFPhK5dNZrHZcMeoOFgoFraysDGvXrkWfPn3O2LsrEKqqoqqqCj/99FOY0pER+MgrwaSlZyG3XXeIYuj/65u27IgGmU0MTEWJ4q233sLJkyeD2otMVVW43W4888wzhswUo/BhoSSYXpeM0H0NQRSQd9loA9JQonG5XHj00Udx5MiRgMZTfD4fHA4HnnjiCRQWFtZBQtKDhZJgMrOawhTi464asmxGZnYzgxJRoikvL8fDDz+MyZMno7y8vNa9yFwuF9xuN+bPn48HH3wQe/fujUBSChbHUBKMxaZ/t2QAuraeIfJ4PJg8eTKmTp2KXr16YciQIadObCwvL8eaNWuwdOlS3bPCqG4lTKGkpWchKSUNgiDC6ahASVFi3j47qsoMuY6zSv+aAiJVVZGfn4/8/PxIRyEDxHWhmM1W9Og/HENG3IbU+plQ/NUDgZIkw+moxJK5/0X+iplwO6sinLTuHNy7FT37Ddd1h+FxO3FgzxYDUxFRPBAuNGuioKBAy8vLq8M4xul1yQiMv/1xaJp23j+eHrcLoijg+2nvY+m8SXWcMDLMZiuee/uHoNegnM7rdeO5Pw2H28UdX4kSkaZp6wD0PvvjcTkof/mICRh/++MwW2wXfCdusdpgMlsxbOy9GHPrw3WYMHK8XjcKVs2B3x/cEbI1FMWPjWsXsEyI6BxxVyg9+l6Fq669O6h34BarDX0uuxaDht0axmTRY8mcL049/guW4vdh0eyJBiciongQV4UiShKuu+2xkB7nWKw2DB93H6wGzYKKZsVFhZj4zmPweoI7htjrceO/7/0VJ47uD08wIoppcVUonXoM1HU0r6aq6H3pSAMTRa+dW1bj0zcfgcftuOhhXz6vBx63ExPfeQxbNyyro4REFGviqlCGjLhd1x2GxWrH4KsnGJgouu3athavPDYOi+ZMhLOqHG6XA/5ftsTw+3xwu6rgclRi6bxJ+Nvj47l/FxFdUNxMGxYlCTktOui+TlJKGtLSs1BWctyAVNGvsrwYP3z7ERZ89yk6dL0UmdnNYLElweN24OTxQ9i2ccU52/ITEdUmbgrFbk+F4vdBNOvbVkTx+2FLSk2YQqmhqgofZxGRLnHzyEvTVMCAwwQFoXoshYiIghM3heJyVkEU9d9wSbLJsO1JiIgSSdwUiqoq2L29AKrOu4viE4WoLC82KBURUeKIm0IBgCXffw5fkGsrTud2Obhoj4goRHFVKLu25cPtDn1LEEEQsDF/gYGJiIgSR1wViqZpmPTBM/B63UG/1utx4etPXoDf5w1DMiKi+BdXhQIAe3asw1cfPQevJ/BS8XrcmD3lHd6dEBHpEDfrUE63MX8BqipLcMs9z8NmT4HJbIUontud1SvDvZjy2UvYsn5pBJKSkcwWG+xJqdA0DU5H+UW3lCEiY8XteSg1WnfojcuvnoC2nftWzwDTNIiSjP27N2HR7InYsWlV9RoWikmSJKNLr8tx+cjb0Cin9a+HqMkmHNq3HYvnfI5tG5ZDVbnan8go5zsPJe4LpYYgCLBY7RAEEW5XFS70fVNs6N73Soy//QkIgnjePdzcLgcUxYevPn4e2zYsr+OERPEpoQ7Yqo2maXC7HHA5K1kmcWDw1RNw42+fhs2ecsENQa22JCQlp2HC719G30Fj6i4gUQJKmEKh+NGz/3AMGxPcIWpmixVjbnkYHbtfFsZkRImNhUIxRTZZMP72J0I6RM1sseKmu56BIPDHnigc+JtFMaV7n6G6HllKsgkduw0wMBER1WChUEwZMlLfIWpWWxIuH3GbgYmIqEZcrkOJB0nJ9dD70lFo0rwtbPZUuF2VOFa4F/krZqGi7GSk40VEcmo6GmQ20X2d5q06Q5JNp6YYE5ExWChRpkmztrhi9J3o2P0yaKp6xliBz+vBldf+Dru25WPhzP9g/+5NEUxa95KS68Hv90E2mXVdx6/4YE9K5a7SRAZjoUSRvAGjcN2ERyHLZoiSdM7nTb+cRtm+yyVo3b4X5n37IZZ8/9+6jhlhBkz55qxxorBgoUSJ3peOxHUTHg1o9pIoijBbbLhqzD0AkDCl4nRUQJJMuq8jyya4nJUGJCKi03FQPgpk57TCuNseC3oqrMViw7Ax9yC3bY8wJYsuleXFKCs5rvs6h/Zv567SRGHAQokCl199GyQ5tHfessmCodfcZXCi6LVozkS4XaGfeeN2ObB4zucGJiKiGiyUCLPak9EtbwgkKbSnj6IoIrddd6SlZxmcLDr9tHoeBEEI+fWq4sfWn5YZmIiIarBQIqxH32FQde8tJqDvwGsNyRPtfF4Pvv3yn/CGcNSz1+PG5M9e4s7DRGHCQomw7JxcWELYRuR0JpMZjZq2MShR9Fu7bAYWzfk8qFLxelyYPeUdbC5YHMZkRImNs7wizG5PMeQ6VnuyIdeJFfNnfIzykhMYc+vD0ABYrfZav656vEXD15++iE35C+s0I1GiYaFEmNNhzPRVt7PKkOvEkrXLv8P61fPQo++VGDLydqRnNIb/l9XvssmM40f2YfHsidi0bjFXxRPVARZKhB0r3AOP2wnLed5hB8Ln9eDIoZ8NTBU7/D4P8lfMQv6KWUhKSYM9qV71EcBV5XA6yiMdjyihcAwlwn5aPQ9CLefdB2vN0hkGpIltjsoyFB07gJPHD7JMiCKAdyi/EAQBbTrmITunNay2JHg9LpSePIatG5aFdRGc2+XAxrUL0LP/8JCmDquqir07f0J56YkwpCMiClzCF4otKRV9L7sGg4bfCrPFBlk2Ve9Eq/jh93kgCE9j9dJvsWLBZJQUFYYlw+I5X6Bb3hUhFYrf58H8mZ+EIRURUXAS+pFXTosOePK16Rg29h6kpmXAakuCbDJDEATIsglWWzIsVjsuHTIef3nxK/TsPzwsOY4f2Yspn70Mr8cd1Ou8Hhe+/+Z97Pt5Q1hyEREFI2HvUJq27IjfP/Y+LNaLrwGp2S79+juehCybsXb5d4bnWf/jXAiCgPG3PwFJNkOqZbfhGqqqwO/zYs7Ud7F8/leGZyEiCkVCFkpSShrueeTtgMrkdGaLDWN/8whOHN0flrNI1q36HkcO7sKQUXegS8/B0LQzz0Pxet0QIGDH5h+xaPZnOLh3q+EZiIhClZCF0m/w2JAPaZJNFgwbey8++Pv9BqeqdvTwbkx6/6+w2VPQ+9IRaNy0LWzJqXA7KnHsyD4UrJyNqoqSM15jtSejZZtup6bMOqrKsHfnevi8nrBkJCKqTcIViiCIGHjVzTCbrSG9XhRFtGzTDWnpWYZspX4+Lmclls//+oJf06RZWwwcdiu65Q2B3+8/tWmipmkQRRH5K2Zh+fyvcfL4wbDlJCKqkXCF0r5rf8iyviNkIQCXDBmHOVPfNSZUkERJwk13PYsuvQZDkk2QJBm13XD1GzwWfQZegxULJmP25LfrPigRJZSEK5TGTdvAbAnt7qSGyWRBi9ZdDUoUHFGUcPdDb6FF6y4XPZBLlk0ATLh0yHikpNbHVx8/XzchiSghJdy0YXtyGkTx/DOoAmU1aFPHYF034dGAyuR0FqsdXXsPxZCRd4QvGBElvIQrFI+rCpqm6r6OL8g1I0ZITctA70tHBn1UMABYrDYMHX0nTGZLGJIRESVgoZQWHw96AeHZVFXByeOHDEoUuEuGjNd9jR79hhmQhIjoXAlXKJvXLdK9GaPP68GqxdMMShQYUZRw6RXX67rDsFjtGDLidgNTERH9KuEKpXozxvlQFH/I16gsL8aBPZsNTHVx9Rtkh3zu/OkaNMwx5DpERGdLuEIBgCVzJ0Hxh1YoHrcLC2f9x+BEF2ezpxhyFrri98EWoQkFRBTfErJQjh3eg9lT3g7qTHIA8Hrc2L5pBdYunxmmZOfn9/tOLVzUQxBF+P3h246fiBJXQhYKAKxYMBnzvv0o4FLxuF3YvmklvvzgmTAnq11lRbH+BZm/8LidhlyHiOh0Cf0wfcn3X+DIwZ9x9bjfIzunFSRRhiT/+p9EVVX4vG44qsqxcOZ/sHrp9IhldVSW4dD+7WjZplvI11AUPzbmL4CmaQYmIyKqltCFAgA/b12Dn7euQcNGLTDgyhvRrGVHWGxJ8HrcOHn8EFYunIK9O9dHOiYAYPGcz9HonudhtSWF9HrF78PSuZMMTkVEVC3hC6XGiaP78c3nr0Y6xgVt37gSPp8npEJRVQXFRYUoPLAzDMmIiBJ4DCUWqaqCz976S9CTCYDq0x0nvvN4GFIREVVjocSY/bs3YeI7jwdcKqqiwOWsxPuv3Y+iYwfCnI6IEhkLJQbt2LwK77x8N/bt2gSv1w2/33fO1/i8Hvi8Huzalo83np2AQ/u2RSApESUS4UIzfgoKCrS8vLw6jEPByshqigFDb0TnnoNgtSVD01S4nVVYv3ouVi2ahvLSE5GOSERxRtO0dQB6n/1xFgoREQXlfIXCR15ERGQIFgoRERmChUJERIZgoRARkSG4Uj5BCYKI9l37Y8DQm5DRsAlMFis8bicKD+zEsnlf4uDerZGOSEQxhoWSYARBwMBht2DIiNsgmyznbOOSkZmDjt0uQ0VZEeZMexeb8hdGKCkRxRoWSgKRTWbc8cBraNW+J8wWW61fI0oSLJINmdnNcPPvnkXTlh0xe/LbdZyUiGIRx1AShCAIuO0Pr6BV+17nLZOzmS02XDrkelx57d1hTkdE8YB3KAmiz2XXoHXHPJgt1qBeZ7HaMGTEBOzYtDKmt29JTk1Hv0Fj0aHrJbAlpUBR/CgvPYE1S2dg60/LDDlemSjRsVASxBWj7oAlwDuTs8myGYOvnoAv3n3C4FThl9U4FyPG/x7tOveHBg1m86+F2rhpG7Rs0x2q4seKBZOxcPZE+H2eCKYlim0slATQonVXJKemh/x6UZLQqftlSEquB0dVuYHJwqtd5364/YFXYTJbIIpSrV9TMylh8IgJ6NxzEN5/7Q8x9T0SRROOoSSAfoPHwmS26LqGpqno1udKgxKFX8u23XHHg6/BYrWft0xOZzZb0bBRC/zhiQ/PuIshosCxUBJAembjgP6oXojZYkNaepZBicLLZLbgrj//M+DJBzVkkxkNMptgzK0PhykZUXxjoSQAo95xW6yhjcHUtR79hoVcoCazBT36Dw/pmGWiRMdCSQBOR6Xua6iqiqqKMv1h6sCQEbfDYrWH/HpNVdH70pEGJiJKDCyUBLB353p4vW5d1/B6XDh8YLtBicInO6cVUtMydV3DYrVjwNAbDUpElDhYKAlg9dJvIUDQdQ2/z4Mdm340KFH41G+QDVX1675OalqGAWmIEgsLJQFUVZRg55YfoapqSK/3etxY+sP/oGmhvb4umc02QGd5AoAkm/SHIUowLJQEMe/bj0JatKeqKnw+N9YsmR6GVMZzOSsBnP9Y60D5dD4iJEpELJQEceTgz/jyo2fh9QT+h1JVVfi8brz/2v0xs9jvWOFeyLJZ/3UO7zUgDVFiYaEkkM0Fi/H5u4/D43ZedJDe43bC6SjHWy/+FkcO/lxHCfWrKCvCvl0bQ368BwBulwOLv//cwFREiYGFkmC2b1yJlx8di4UzP4OjsgxuVxW8Hhf8Pi88Hhd8Xg9czkocPbQbG9cuRKt2PWFPqhfp2EFZPOdz+DyukF+v+H3YvnGlgYmIEoOgaed/3lxQUKDl5eXVYRyqS4Igom2nPshp2QHtO/dH09yOUFUNltN2JPZ6XBAEEVt+WorFcz5H4YGdEUwcGEEQ8PAL/0NmdjPIQQ6uezwuzJ7yDlYumBymdESxT9O0dQB6n/1x3qEkME1TIQgirhh5B5q16gyTyXJGmQDVW66YzBZ0630FHnjyIwwYekOE0gZO0zR88Pf74XRUQPH7An6dx+PChjU/sEyIQsRCSWDtu1yC2x94FRar/aLv5EVJgtliw8jrH8DAYbfUUcLQVZYX441nJ6Dk5FF43M4Lfq2qKPB6XFi9+BtM+c9LdZSQKP5w+/oEVb9BNm67/5WgD9wyW2y4+rr7UHhgB/bsWB+mdMaoKCvC60/fjO59rsSQkbejfoNsCKIEWTZBU1X4fB6IkoStPy3H0rn/xcG9WyMdmSimsVAS1IChN0KUQvvfb7bYcNWYe/De3+4zOJXx/D4vClbORsHK2chp0QEt23aH3Z4Cv9+LyooSbF2/NGamRBNFOxZKApJN5uozUkyhr9dontsZ6RmNUXLyiIHJwuvw/u04vD/69yMjilUcQ0lAXXoO1n8RQUD/IeP0X4eI4gYLJQFlNW6pa3t3ADCZzMhp3t6gREQUD1goCciWXA+CoH8DRZs92YA0RBQvWCgJyOWo1LU1SQ23y2FAGiKKFyyUBFR84rDu3XQVvx/Hj+wzKBERxQMWSgLaVLAQgqDvf72i+rFq0TSDEhFRPGChJCCP24n1q+dC8Yd+suHxwn04foRbvBPRr7gOJUEtnTsJPfsNgyQH/yPgcTsxf8bH5/28KcmGhh1awZKSBFVR4CqtwIltu6Ep0X/iIxGFjoWSoE4c3Y+pE/+G8bc/DrPFFvDrPG4XflzyDbZuWHbO59JbNUPXG0cg9/K+UHx+CAKqD08UAFVRsfWbH7BtxkK4issM+z6IKHqwUBLYulVzIEkyxv7mL5BNZojihZ+Aejwu/Lh4GmZNfuuMj4smGUOevh9N+3WDJMsQZQmy5dxV+N1uHoVuN4/Cmve/wtZp8wz9Xogo8lgoCW7t8u9w9PBuXHnNXWjbuS80TYPZ/OuGkX6/D6qq4MjBXVgw85NzDp4SZQmj/vUkGrRpAZPVcsF/q6Zk+txzA6xpKVj3yVTjvyEiihgWCuHQvm349M2HkVKvAfoOHIOmLTvAlpQCj8uJE0f348cl03Hy+MFaXzv4yfsCKpPTmWxWdL1xBCoOH8OueSuM+jaIKMJYKHRKZXkxFsz8JOCvr9e0EVpc1rvWx1sXY7Ja0O/+W7F7/kpo6vlPDSWi2MFpwxSyzuOHQbjIuMuFSCYTmvbtblwgIoooFgqFRLKY0Xb4QEim0G9yzUk2dLtllIGpiCiSWCgUkvotmkBTFN3XyeyQa0AaIooGLBQKiSUlCZqmf+xDkmUIEn8MieIBf5MpJKov9G1bzsYV9ETxgbO8KCTOknKIIWzbcjavw2VAGjpbkl3ELaPSMOyyFGSkyfD6NBw44sXE6aVYsY7HDlB4sFAiIKtzW3S9aQSa9OoM2WYBNA0+pxt7l6zBlilzUbq/MNIRL6r80FE4i0tRLyc75GsoPh/XoRisWWMTnry3IX5zTTpUTUNKknTqc6qq4cYRaThZquDVj07g4ynFMGAYjOgU4ULPwQsKCrS8vLw6jBPfmvTqhMseuQu29HqQzGaIZ40dqH4/VL+C0gOFWPLyByjddzhCSQPTftTl6P/ArTDZA98L7HR+jxdT73gcFYXHDU6WmPp1t+P7j3ORZBVhMl34RE6HU8HazS6Mvm8fHE4+cqTgaJq2DkDvsz/OMZQ60nb4ZRj2ysNIbZIFk816TpkAgCjLkK0WNGjTAmPeew6NuneIQNLA7V6wCkBoRwmrioKTO/exTAzSvYMN8z/NRVqKdNEyAYAku4R+3eyY90luQF9PFAgWSh1o1r8HBjx0J+QAtycRRREmuxXD//YI0nObhjld6PxuDxa98G/43Z6gX+tzurDoxXfDkCrxmE0Cfvg0F3ZbcL/ONquI7h2seOWh0B9bEp2OhRJmktmEIc/cH3CZnE62mnHF/z0YhlTGObByPVb88z8Bl4rqV+CpqMKsP72MqmMnw5wuMYwfXg8WswBRDP5OI8km4d6bGsBm5V0K6cdCCbPcwX1DfSoEQRSRnJWBzPbRvfjv57nLMffx11G6vxA+lxtqLSO9fo8Xfo8Xheu3YtrvnkLx7gMRSBqfHru7IVKTpYt/4XloGnDTyPoGJqJExVleYdbt1tEwhzhoDQCSWUaXG67Gouf/bWAq4x1Zvw1TbnsUGe1aoutNI9GkVyeYbFZoqgpPpQO75q3Atm8XwFFUEumocaV9rgWtmgV/93u6lCQJD92Rgf9M4/8b0oeFEkZJDdOR2rihrmuIkoSWA2Nnpt3Jnfuw6Ll3Ih0jJBazgPHD6+GR3zZEm+ZmWC0iPD4NR0748ObEInz+bSkqqqJrRlSrZmb4fBoQ+nsWAEDTRsHvGE10Nj7yCiNbWioUA1aUC5IIyWwyIBHVRhCAZ+5viBM/dsK7z+agewcbkuwSJEmA3SqidTMLXnm4EY6u7IQPns+B1RI94w1JNqn6qGWdoul7otjFO5Qwql5Jrn+/K03VIJpkKF6f/lB0BlkGpr3dAkP6JSPZfv5xiJrP/eaaNOR1tePyCbtRXhn5u5VKh2LInmouN8+kIf14hxJGnkqHrvNCaoiSCJ/TbUAiOttnrzTDFf0vXCans9skdMi1YO4nuTBHwfqNHXs9sJj1/4ztOhD81G+is7FQwqjiyHGofv17WxT9vL96Kg4ZavjAFFw7NBVJtuBmSFktIrq0teH+3zQIU7LA7TvsxU/b9O2HVlGl4O+fnDAoESUyFkoYaYqKLdPmwe/xhnwNr8OFjZNmGpiKajz2u4YB35mcLckm4uE7GxoyfqHXqx+fQEVV6G9cVBX4dkGFgYkoUcXEGIrZYkPPfsPRd9C1SElNhyhJcLsc2L5pFVYumIySk0ciHfG8tn+3CN1vGR3y6zVVxf4V6wxMRADQvIkJfbvZdV0jJUnEkH7JWPhjlUGpQjN7SQVKyhXYrQJkObj3iFVOBa9/cqJ6phiRTlFdKDZ7CkZcfz96XzICmqbBYv31D0C9+kB6ZhNcOmQ8Du7dgu++ehOH92+PYNrauYrLsG3GQnQYfTlMNmtQr/W5PPjx3/815GREOtP4YWkIYWH5GZLtIu4clx7xQlEUYMhte7BuehukJgmQpMC+MYdLxYJVVXj5Az7uImNE7SOvtPQsPPT8JOQNGA2zxXZGmdQwmcwwmS1o1b4X7n/iA3TuOSgCSS9u9b8n4XD+FvhcgQ+s+1xubJ7yPX6esyyMyRJXkywTLBZ9P/6iKCAnKzqmc+877EW/G3bjRIkfTtfFZ59VORR880MZrv/Tfg7PkWGislDsSfXwwFMfo179TJhMgS24MltsuPXeF9GmQxQuAtQ0LHjmX9g5Zxn8Hu8Fp//63R74PV6s/fBrFHw8pQ5DJhaL2ZjBj2iY6VXj530edByxE8/9+xiOn/SdM67i9apwuVUsy6/CTQ8dwG2PHoLfuIM3iaLzkdf1dz6JlNR0SFJw8cwWK+7449/x3J+Gw+uNrmm2mqph1ZsTsXnyHHQadxXaj7ocULVTawgEUYTi9WHz5O+xY9ZiuMsrI5w4vh0/6YeqaiFtqHi64rLo+otcVqHgtY+K8PePizDsshQM7J2ErAwZLreGQ0d9mDK3DHsPhT5JhOhCoq5QklPT0aHrpZADvDM5myAI6N7vKqxd9p3ByYxRebQIq9+ZhPwPJ6NB6+awpCRBU1W4yytRvPsANJXPH+rCsoIqOFyZZ5xoGKwqh4JZS6Kz+DUNmLusEnOXRWc+ik9RVyj9Bo2FpmN1ucVqx5ARt0dtodRQvD6c2LY70jES1pI1DpRVKLoKRRQFTJpZamAqotgWdWMo/S8fC7M5uNlQZ0utn4msxtG95TtF3t8/KYLDFdoMOp9fw6SZpahyRH77FaJoEXWFkpyi/1wGVfGjXv0MA9JQPPvPNyUoKlHg9wdfCg6Xipfe5/HFRKeLukIRpdAfQZzOpPMuh+JflUPF4Am7UVqhwhdgqaiqhiqnguF37cWBQm7WSXS6qCsUn9eYTepcDg5G0sUdKPShx5ifseegFxVVCtQLTIqoqFJQVOrHgJt3Y81GZx2mJIoNUVcoRw7u0n0N2WTGscK9BqShRFB43IeOI3Zi/B/3Y9HqKrjcKsoqFJRV+FFWUb1QcN1WJ37310PIGbgNG3dE15R0omgRdbO8Fn//ORo1fR5WW1JIr1cVBds2rIDTUW5wMopnmgbMX1mF+Sur0LihjLYtLUhNluBwqthf6MWeg1G6dkMQkNW5DZIbNoBsMcPrcOLkrgOoPMLtVKjuRV2hbN+4Eoo/9GfTPp8XS+b+18BElGiOnPDjyInoWrB4NktKEtqNHISuN46EbK0+U14QBWiKCtEk4+TOfdj4v1k4+ONPXNtEdSbqCkVVFcz55j1cc9OfYbEEd1C2z+vBkYM7cXDPljClI4q85pf2xJBnHgAAmGyWWr8mu2s7pLdqBmdxKWb96SU4i8vqMCElqqgbQwGA1Yu/Qf7y7+DxBH5wkN/nRXlpET5+48/hC0YUYa2u6I8hzz4Ak81y3jKpYU6yIaVxQ1z3ycuwZ+ifjk90MVFZKAAw/b+vY+nc/8LrcUO5yA52bpcDRw7twr+euw1ul6OOEhLVrYYdW2PQY3fDZL1wkZxOkmVYUpMw+s2/QjRF3QMJijNR/RM2b/qH2LBmPi676mb06n81VNUPUZQhCAIUVYEkSji4dysWf/85dmz6EZrGVcsUv/red9Op8ZJgSLIMW4M0tBzUB3sWrApDMqJqgnaBwxAKCgq0vLzo2A7ebLGhfZf+SE5NhySb4HJUYO/PG1BSVBjpaERhl9IoE9d//hpkS2ibpgJA8e6DmPbbJwxMReGWmizitjH1cd1V9ZBeT4aiajh20o/Pp5fim/nlETtpU9O0dQB6n/3xqL5DOZ3X48KmgkWRjkEUEZ3GXQVB5wH2qTlZSG/VDCV7DhqUisKlWWMTnnswGzeOSIOiaki2n7mDyICeSfjg+Ry897+TePn9E6iMkj3lonYMhYh+1aRnZ0hm/adDZnVqbUAaCqfeXWzYMKMtfnNNfdis4jllAgCpyRLqpUj48+2ZWP9tWzSJkpNDWShEMcCcHNwU+tpIsgRz8rlHaVP06NjagoWftUL9VBmyfPE7UqtFRPPGJqz8qjXq1zNmH0Q9WChEMUDx6V9oqaqaIdeh8BAEYO7HuUi2B/dn2SSLyM6Q8flrTcOULHAxM4ZClMicJ0uR1rSRrmuoPj8XOOqUlSHjtjH10am1FanJEkor/Niw3Y0vZpSirCK0s3VqDLssBfVSpJCOpbaYRVzRPwVNskwoPB65XbBZKBRWyVkZyGzfEuZkOxRv9R+0oxu3Q1OiYxAxVmybsRAZbVvCnBT6oy9REnHwx58MTJU4+nW344l7G+LKS1OgaYDd+utdhMOp4NVHGmHaD+V49aMT2PJzaJuHPvq7hkhN1vHYSgP+cEsDPPXGsdCvoVPMTBumGCIIyMnrgm43j0JW5zZQfX4IkghN0wBNg6qo2PrND9g2YyFcfMccEFGWMOG792EJcQxE9fuxY/ZSrPjHpwYni38P/zYTz/0xGzaLcMG7B79fhccH/O6pQ/hqdllQ/0bDBjL2L+4Am0XfKERxmR8ZfbfqukYgYn7aMMUGS2oyRv7jcaQ2zYbZ/su76VrWTnS7eRS63TwKy//xH+yau6yOU8Ye1a9g2/T56HLD1SGtRVH9KrZMnRuGZPHtL3dl4pkHspBku/gfelkWIcvAxy81haoCk78vC/jfadrIBI9Xw0V207mo+qkSRBFQI/QAgIPyZBhLajKu+/gl1G+Z82uZnIdsMUO2mDHgoTvQadywOkoY29ZPnI7yQ0eDHlj3udxY99k0lB04EqZk8WlgXhKefTC71mm7F5JkE/HpK03RLjfwdrBbxeozFHRSFMAeQPmFCwuFjCEIGPnPx2FPrxfUegmT1YK+996Ipn27hTFcfFC8Psz688soO3gEfk9g57P4XB5smToPG7+cFeZ08eeZ+wO7M6mN2STg4TszA/76iipF98JVABAlwOGM3PgkC4UMkZPXBak52SEtvpOtFvS7/9YwpIo/nooqfHvfs9gxawl8Lg+8znN35FZVFT6nG5XHirDs1Q+R/9HkCCSNbc0am3BJj9AO+QMAkyzg1tH1kZwU2J/YPYe8MJv0F8r+Qq8RNzoh4xgKGaLbLaMu+pjrQpKzM5DRriVO7txnYKr4pHi8WPXmRKz94Cu0uqI/Oo+7CvaM+pBMMnwuT/XhWl/NxrGNOyIdNWbdc0MDhDB79wyqpuHGq9PwydSSi35tlUPF5O/LcOvoNMhyaO/zqxwK/v5xUUivNQoLhXRLzspAVqc2uq4hmWR0vXEEFj3/b4NSxT+/24Ods5dg5+wlkY4Sd7q1t8Kic8ZVsl1Cx9aBj6O88VkRxg9PgxziX2VRFDBpZmloLzYIH3mRbpntc6Fe5MyaixElCdld2xmUiEifeinGbGOSXi/wdti4w438TU64PcGPgVQ5Ffx70klURXiTSN6hUEDSWzVDg9bNYE6yw+/xoOrYSRz5aRs0VYM52Q5B1P/exGSzGpCUSL+KKn2r3muUlAf3RuvaP+zD+ultkZNtgsUc2O+Uw6VgxToHHv/H0VAiGoqFQuclmU3IHdwX3W4ZhZRGDaFpKkRJgqao0DQVis+PLVPmwlPpwIUWyAZK5T5TFCU27nBj6CUpAf9Rr02VU8H2PZ6gXlNRpaLP9buw4LNWaN3MjJSk898pqaoGp0vFrCUVuO2xQxFbe3I6FgrVKrVJFka99VeYk2wXHGzvMeFaQBAMKRRXabnuaxAZ4cPJxfh/dwQ+7bc2oiDg6zllQb+upExB3+t3YdxV9fDY3Q3RpoUFkohTs8DcHg2iCCwrcOD1T05g/soqXTmNxEKhc9TLycaYD56HyW6FKF34WXLNkbR6C8XncmPrtwt0XYPIKAcKfVi90YnL+yaH9HqfX8NXs0tDPvjK59Pw1ewyfDW7DF3aWTG0fzLS02T4fBpOlvrx3aIKHD4WuU0gz4eFQmcw2awY9dZfYbLbIEqB3+4Lv9ylhLo4SxAE7P5hZUivJQqHF949jj5d7SEtbvT6NLz+qTFTeDfvdGPzztA2nKxrnOVFZ2h91aUwJwVXJnr5vV7sXrAKPlds/NJQYli8ugovvXscDmdwA/QOl4p7nj4U9PhJPOAdCp2h+62jQ55tFcpdiuL3w3GiBD++Mymkf5MonF758AQUVcMzD1x8t2FF0eD2arjvmUP4cmZZ3YWMIrxDoVOyu7aDNTVF1zVURQl4TYrf60XVsZOY+eAL8NWyhQhRNHjt4yIMu2sv5i6vhMujwuU+c1zE4VLgcquYMrcMl960C//9riwyQaMA71DolMwOrSDK+hZ0SbKM0gOFAIDkhg0gmU3nDOz7nG4IooDdC1bhx3cmsUwo6q1c58DIe/ahcUMZd1yXjk5trKiXLKKkXMFP212YOL0UJWXGrF2JZSwUOsWcbNddKDWmTHgUme1z0eWGq5HdtR1MNitUvx+u0gps+3YBds1bwTETijlHTvjx8vsnIh0jakV1oaQ0yoS1XgoEUYSn0oGKwmPQ1AhupRnnFI+3egxE93WqpzMW7djLvbmIEkjUFYpstaD10EvQ7ZZRSMqoD8WvANAgihL8Xi82T5mLHTMXw11WEemoccdxshR+j1fXrsEAUHX8pEGJyGiCKCCnT1d0u3kUGrRuDtlqqb5zLCnH1unzsXPOUnirnJGOSTEqqs6UbzWkHwY+djegaTCd54+a3+0BBAGbvpqNgk+m1lm2RGBKsmHCt++GdMRsDa/ThQXPvIXDazcZmIyM0G7UYOT97gbIFjPMSef+fvlcbgiiiL1L1mDlGxM5tkXndb4z5aNmllencVdh4OP3wGSznrdMgOo7GNliRpcbrsbgJ++tw4Txz+dwYe/i1VB07Bzsd3twOH+zganICP3/OAGXPDgB9vR6tZYJUL2oVbaYkTu4L677+EXY0uvVcUqKdVFRKC0u640+994EkzXwswNMNitaDuqDXneND2OyxLPp6++h+kObreJze7Dpq9mGnI1Nxul15zi0Hzk44PVFssWMlOwMjH7rr5Btgf9OEkW8UARRwICHfxtUmdQw2azodtNIWNNSw5AsMZXsOYj1E6cHPQPL7/GiaMdebJ4yN0zJKBT1c5ui280jg16sKsoykrMy0Jtv2CgIES+UnD5ddT2zh6ahw+jLjQtE2DhpJrZMnQefO7CtI/xuD4p27sPcx/4OTYmCPbTplC7XD4cQ4lRw2WJG+1GXQzKbDE5F8SrihdLt5lHnfaYbCNlqQefrh0PQewA0nSH/o8lY8tL7KD98DD6XG2otReF1uOCpdGDj/2Zj1p9egt+VeHsXRTOT3YbWV/SHFOqZsr/IHdzXoEQU7yI+bdiIY18lswn1mjZC2YEjBiSiGvuWrsW+pWvRsGNrdLlxBDLbtoDJboPf40XV8ZPYMm0e9i9fB03hCuFo1Kxft1rfCATDbLehwzVDsOuHFQalongW0UKRLGbAgPFbTVVhSQnt3AK6uBPbdmPhs29FOgYFydYgzZCdDzjbiwIV2UdemgaEeH7GOZeKhvMviaKIKEkQRP2/4qLOR2aUOCJaKIrXZ8jjElGS4S6vNCARUfzwVjkD3vn5YtchCkTEB+UPrFoPVWepuErLUVF43KBERPHh2OadIZ+gWcPv9eFQPnc9oMBEvFA2fTUHijf0s5F9Ljc2fjnTwERE8aHswBGU7Dus7yKahm3fzDcmEMW9iBfKiW274TxZGvoFBAG75vMscqLabPxyFrw69uQ6vuVnbvZJAYt4oQDAohfeDXgR3el8bg+W//0Trn8gOo/9K9ah6mgRFF/wYyk+twdr3vtfGFJRvIqKQinasRcLnn4zqFLxuz0o+GQqdvPuhOi8NEXBrP/3Clyl5UE9Wva7PVj8wrs4+fP+8IWjuBMVhQIAh9ZsxKw/vXjBldlA9epsV1kFFr/8PjZ/PaeOUxLFHndZBabd9SRK9x2G1+mCeoEp9l6nG16nC/Oe/Cf2Ly+ow5QUD6LqPJQaDTu1RrebRqLZJT0hiEL1KYKCiONbd2HjlzNxaPWGgE9urN8yB13GD0dW17YwJ9mheH1wnCjB1m/nY/+y/JB31iWKRY16dES3m0eicc+OULw+CBCgQYMoSXCVVmDjlzOxe/4qHs9MF3S+81CislBOJ5lNECUp6B/wJr06oe/vb0Fas0YQZfmcFcNehwvQNGydPh/rPvsGagjPmIlila1BGtJzm8KSbIff7YWjqATFuw9EOhbFiPMVStQvgVW8PigIblpxx7FXot/vb4Z8gS3xazak7HL9cOTkdcHsh17hAi5KGK7iMhQWl0U6BsWZqBlDMUqb4Zeh70XK5HSy1YL03KYY+c8nIJqivl+JiKJWXBVKUsN0XPbQnUEf1iWZTajfogl63XldmJIREcW/uCqUjmOuDHmzSdlqQacxV/IuhYgoRHHz11OUJXQcM1Tf6Y+CgJYD87Bn4Y/GBaOEkdmhFZIy60Mym+GtcqJ49wF9u0AQxZi4KZQmvbvo3gnfnGRDp+uuZKFQwMzJdrS9eiC63jSyeqKHqgFC9ckMkknG0Q07sPF/s3Bk/dZIRyUKu7gplKTM+oac/ZCUkW5AGkoEzS/tiSHPPABAg8lmrfVrcvI6I6tLG1QcOobZj/wNnvKqug1JVIfiZgxFMpshSPq/HclsMiANxbtWV16CK559ACab5bxlAgCCKMJst6F+yxyM+/hlWOul1GFKoroVN4XidTih+vWf2qhnZ1ZKDFmd22LQX34X8NR0oPqNii09FSPfeMKQNz5E0ShufrJP7twHQdQ3iKL4/TixdZdBiShe9fvDLUGVSQ3JZEJK44Zo1r9HGFIRRV7cFErpvsMoP3RM1zVUv4LNk+calIjiUb2cbDRo0zzk15vtNnS7ZZSBiYiiR9wUCgBs/HJm9R5dIaooPK57PyPJbArp3SvFhk7jh+me/JHRpgVSc7IMSkQUPeJmlhcA7FuWj7y7b4BsMZ+zGeTF+HUcJpTZIRddbxyJFgN6VT8f1wAIwPEtu7Dxy1k4tCbw3ZEpuuX07gxJ5+JXVVGQ1bktKg4fNygVUXSIq0JRfX7M/OOLuO7jl2BJtgdcKj6XBwUfT8bhtZuC+vcy2rbAkGfuR1Jm+qldkU/XqFt7NGjdHIrPhxX/+A/2LV0b1PUp+tRsKqqHKEmwJNsNSEMUXeLqkRcAOE4U45vfPYWqE8UXnbHl9/rgd3uw6q2J2DwluLGTJnldMPrtZ5DWrDFMNus5ZVLDnGSDLS0Vlz91H7rcOCKof4OijxHn52iaFtKRvETRLq7uUGo4ThRj8oS/oOWgPuh+yyikNskGoEGQJGiqCs2vAIKA7TMXYes3P6Dq2Mmgrp/RtgWuevH/wWQLfKxEtlrQ+67xcJdVYNe8FUF+R/FFlCVYUpIgyjI8lQ74gzj6OdKcxWVIzsrQdQ3Nr8BVUm5QIqLoEZeFAlQ//tqzYBX2LFiF9NymyGyfC3OyHX6PF86TpTi0dlPIh2pd/vQfgiqTGiarBQMe/i32LytIyBPxak4LzOndBaqiQNM0SLKMiiPHsWHSTOxZtBqKxxvpmBe0/btFSGvRBGZ76I++BEnEoSAfrxLFgrgtlNOV7D2Ekr2HDLlWZvtcJDdsEPoFVA2tr7oU22csNCRPLMjs0ApDn/sjLKlJMFktEETxjPGttGaNccmfbsOlf74d+R9Nxpap8yKY9sJ2L/wRl/zptpBfr/j82DlnWdQXJ1Eo4m4MJdy63DhC1/YsJrsV3W8ZbWCi6JbTpytGvfkUUrIzYLbbzjvl1my3wWSzIu/uG3DJH0P/gx1uisdbXQje4E4RraGpKrZM41onik8slCC1GNDrvAPwgbKmpaJeTrZBiaJXRruWuPKFPwd14JnJZkW7kYPQ/TfXhDGZPvkfTUbV8WIo/uAemfpcbhR8OpXThSluJcQjL6OIsqR7DQIAqIof1rQUlB/Wt7L/fKxpqWg1pB+SszNgslngKqvEyZ37cPDHn6Ap+vc7C9Sgx+8JbazJZkXP28di55ylUTl47XO6MPOPL2D0O88gKbM+ZPPFz+DxuTzYMnUeNv1vdh0kJIoMFkoQBFGEpmnQeezKqWsZrWGn1uh2yyg07dMNmqadujPQNA0+pxuqX8GWafOw7dsFcJdVGP7vn65B6+ZIbaJnNbiGDtdcgfWffWNYJiM5i8vwze+ewiV/vA2thvSDpmrnlKemqvC7PfBUubDmg/9hz/xVEUpLVDdYKEFQvL7qk5N0EkQRnkqHAYl+1evOceh608hfFlieWVaCIJxakNf91tHoMn4YZj/0Ck7+vN/QDKfrcsNwSHLoP16yxYLO44bhpy++rdO7qmD4HC4sfeUD/Pj2F2g7/DJ0HHslbPVTIZlM8LncOLlz3y+Ha22LdFSiOsFCCdLRTT+jSc+Ouq6h+hWUHTxiUCKg7+9vRscxQwN6vCRbzJAtZox++2l89+ALKA5TqTS/tFfQ29+cTZQlZLRpgaIdew1KFR7eKie2TJ0X1bPTiOoCB+WDtOl/s3SdmeL3eLBl2jzD3nW3HNwHHcdcecFDnmojW8wY+Y8nYNKxnuJCTPbg8tRG01RYUpMNSENEdYGFEqRDazfpXNktGLoGJe/uG0Ia+BZEEZJZRpthAwzLcuY/YMBIk1b9uI6IYgMLJViahuWvfwpfCKXic7mxZeo8w2YuZXZohaSM+iG/3mSzotvN4Tmbw2/ATgCCIMBdwTPYiWIFCyUEB1asQ/6HXwdVKj6XGwdWrsfaD74yLEeXG67WtcgSACypScju2s6gRL86uHojVEXfRoqapqF4135jAhFR2LFQQrRl6jws+9uH8Lk98F1gTMXv9sLv8WLLtB+w6Pl/G5qhQetmuhdZCoKAtOaNDUr0q81fzwl5NTkA+D1ebPt2gSG7+xJR3eAsLx32LFqNAz/+hNZXXIJut45GUoM0KH4FgAZRlKD4fNgydS62z1wclgV6wQ7E10aUZUPO+Dhb0Y69cJwo0VVW275dYGAiIgo3FopOfpcHO2Ytxo5Zi5HSKBPWtBSIkgRPpQPlh4+FdQ2F34ANBlVFgc8Vnu3jl772EUb84/Ggtl4BfhlrmvYDHEUlYclFROHBQjFQ5dEiVB4tqrN/r+LwcdRrkqVr1b2mKGHLfHzzz1jy0vsY/NR9AZeKz+XGvqX5yP/w67Bkqmv1c5uiXk4WTDYrfE43yg4eRdmBwkjHIgoLFkoM2zJ1LrK7tdN1NofqV1C4bouBqc60b+laeCqrMPS5P17w8ZrP5YYgitj01Rys+8+0sOWpC5LZhNzL+6LbLaORkp0BVVEhiAI0VYMoSagoPI4NX87EvqVrQz6ThygaCdoFthIpKCjQ8vLy6jAOBUUQcOu0t0OeOux3e7Fh0gysn/itsblqIUgimvXvge63jEZGuxbVR+Bq1avhPZUObPpqFnbOWQZvlTPsWcKpQevmGPHPxyGZTRcseq/DBb/Hi9l/fgml+3nHQrFF07R1AHqf/XEWSozrNO4q9LnnxpAG6H0uN7666f/BVRrejSLPZk62w5qWAumXI4CdJeWG7JEWaZkdcjHqjacgW80BPYZUVRV+lwczH3wBxbsP1EFCImOcr1A4bTjGbf1mPgoLtgS90NLv9mDBs2/VeZkA1XtfVRw+jtL9hXAWl8VFmSRlpldPQLBbAx7TEkURJpsFI//1JGzp9cKckCj8WCixTtOw4Nm3cGj1hoDOqVcVFT63B4teeBeHVm+sg4CJoetNIyFbQtsCR7aY0XncsDCkIqpbLJQ4oPoVLHjmLax44zOUHTwCn8t9zip1n9sDv8eL/csLMOP3/4f9ywsilDb+SGYT2o8cHPLha7LFjI5jhkLQuUiVKNI4yyuO7Jq7HLvmLkdm+1x0GD0EKY0bQraa4amowrFNP2PHrMVwl1dGOmbcyb28LzToe2wniCJaDuyNvYvXGJSKqO6xUOJQ0Y69UX+GSDxp0quTrqnbAGBOsqFR9w4sFIppfORFpJO1Xooh17HVTzXkOkSRwkIh0kkxaHGiEVvpEEUSC4VIp8qjRbq36lf9flQdO2lQIqLIYKEQ6bRr3grddymqX8Wu+SsNSkQUGSwUIp2Kdx9A5ZETuq5Rsu8Qyg8eNSgRUWSwUIgMsGHSdwEtLK2Nz+XGhknfGZyIqO6xUIgMsHvBjygs2AJ/kFvg+NweHFi1HvuXcaEpxT4WCpERNA0Ln3sHRzftDPhOxedyo3DdFix56f0whyOqGywUIoMoXh/mPvoaNv5vNjxVDngdrlq/zutwwVNZhZ++mIEfnnwDql/fDDGiaMHt64nCQJQltBiYh643XI2UxlmQLSb4PV5UHD6GTV/Pwf4V66HpnGpMFCnn276eW68QhYHqV7B30WrsXbQ60lGI6gwfeRERkSFYKEREZAgWChERGYKFQkREhmChEBGRIVgoRERkCBYKEREZgoVCRESGYKEQEZEhWChERGQIFgoRERmChUJERIZgoRARkSFYKEREZAgWChERGYKFQkREhmChEBGRIVgoRERkCBYKEREZgoVCRESGYKEQEZEhWChERGQIFgoRERlC0DTtQp8vAnCgjrIQEVFsaA4g8+wPXqxQiIiIAsJHXkREZAgWChERGYKFQkREhmChEBGRIVgoRERkiP8PFLYKtMbMN5UAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 504x504 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from torch_geometric.utils import to_networkx\n",
    "from torch_geometric.datasets import KarateClub\n",
    "\n",
    "dataset = KarateClub()\n",
    "data = dataset[0]\n",
    "G = to_networkx(data, to_undirected=True)\n",
    "\n",
    "visualize(G, color=data.y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "DataBatch(edge_index=[2, 4120], x=[1094, 21], y=[32], batch=[1094], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 4182], x=[1038, 21], y=[32], batch=[1038], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 3900], x=[992, 21], y=[32], batch=[992], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 3552], x=[984, 21], y=[32], batch=[984], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 3880], x=[980, 21], y=[32], batch=[980], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 4082], x=[1068, 21], y=[32], batch=[1068], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 3612], x=[909, 21], y=[32], batch=[909], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 3446], x=[987, 21], y=[32], batch=[987], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 3986], x=[1049, 21], y=[32], batch=[1049], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 4100], x=[1094, 21], y=[32], batch=[1094], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 4096], x=[1049, 21], y=[32], batch=[1049], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 4072], x=[1065, 21], y=[32], batch=[1065], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 3892], x=[1025, 21], y=[32], batch=[1025], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 4026], x=[1030, 21], y=[32], batch=[1030], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 4206], x=[1122, 21], y=[32], batch=[1122], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 3824], x=[1055, 21], y=[32], batch=[1055], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 3694], x=[943, 21], y=[32], batch=[943], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 4672], x=[1286, 21], y=[32], batch=[1286], ptr=[33])\n",
      "32\n",
      "DataBatch(edge_index=[2, 3222], x=[810, 21], y=[24], batch=[810], ptr=[25])\n",
      "24\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\anaconda3\\envs\\graph\\lib\\site-packages\\torch_geometric\\deprecation.py:12: UserWarning: 'data.DataLoader' is deprecated, use 'loader.DataLoader' instead\n",
      "  warnings.warn(out)\n"
     ]
    }
   ],
   "source": [
    "# Mini-batch\n",
    "from torch_geometric.datasets import TUDataset\n",
    "from torch_geometric.data import DataLoader\n",
    "\n",
    "dataset = TUDataset(root='/tmp/ENZYMES', name='ENZYMES', use_node_attr=True)\n",
    "loader = DataLoader(dataset, batch_size=32, shuffle=True)\n",
    "\n",
    "for batch in loader:\n",
    "    print(batch)\n",
    "    print(batch.num_graphs)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GCN(\n",
      "  (conv1): GCNConv(21, 4)\n",
      "  (conv2): GCNConv(4, 4)\n",
      "  (conv3): GCNConv(4, 2)\n",
      "  (classifier): Linear(in_features=2, out_features=6, bias=True)\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "# GNN\n",
    "import torch\n",
    "from torch.nn import Linear\n",
    "from torch_geometric.nn import GCNConv\n",
    "\n",
    "class GCN(torch.nn.Module):\n",
    "    def __init__(self):\n",
    "        super(GCN, self).__init__()\n",
    "        torch.manual_seed(12345)\n",
    "        self.conv1 = GCNConv(dataset.num_features, 4)\n",
    "        self.conv2 = GCNConv(4, 4)\n",
    "        self.conv3 = GCNConv(4, 2)\n",
    "        self.classifier = Linear(2, dataset.num_classes)\n",
    "\n",
    "    def forward(self, x, edge_index):\n",
    "        h = self.conv1(x, edge_index)\n",
    "        h = h.tanh()\n",
    "        h = self.conv2(h, edge_index)\n",
    "        h = h.tanh()\n",
    "        h = self.conv3(h, edge_index)\n",
    "        h = h.tanh()  # Final GNN embedding space.\n",
    "        \n",
    "        # Apply a final (linear) classifier.\n",
    "        out = self.classifier(h)\n",
    "\n",
    "        return out, h\n",
    "\n",
    "model = GCN()\n",
    "print(model)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "ename": "RuntimeError",
     "evalue": "mat1 and mat2 shapes cannot be multiplied (34x34 and 21x4)",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mRuntimeError\u001b[0m                              Traceback (most recent call last)",
      "\u001b[1;32mD:\\Temp;\\ipykernel_11144\\1363450205.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m     14\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     15\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mepoch\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m401\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 16\u001b[1;33m     \u001b[0mloss\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mh\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mtrain\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     17\u001b[0m     \u001b[1;31m# Visualize the node embeddings every 10 epochs\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     18\u001b[0m     \u001b[1;31m# if epoch % 10 == 0:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\Temp;\\ipykernel_11144\\1363450205.py\u001b[0m in \u001b[0;36mtrain\u001b[1;34m(data)\u001b[0m\n\u001b[0;32m      7\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mtrain\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      8\u001b[0m     \u001b[0moptimizer\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mzero_grad\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m  \u001b[1;31m# Clear gradients.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 9\u001b[1;33m     \u001b[0mout\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mh\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mmodel\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0medge_index\u001b[0m\u001b[1;33m)\u001b[0m  \u001b[1;31m# Perform a single forward pass.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     10\u001b[0m     \u001b[0mloss\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mcriterion\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mout\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mdata\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtrain_mask\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0my\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mdata\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtrain_mask\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m  \u001b[1;31m# Compute the loss solely based on the training nodes.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     11\u001b[0m     \u001b[0mloss\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m  \u001b[1;31m# Derive gradients.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\anaconda3\\envs\\graph\\lib\\site-packages\\torch\\nn\\modules\\module.py\u001b[0m in \u001b[0;36m_call_impl\u001b[1;34m(self, *input, **kwargs)\u001b[0m\n\u001b[0;32m   1108\u001b[0m         if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks\n\u001b[0;32m   1109\u001b[0m                 or _global_forward_hooks or _global_forward_pre_hooks):\n\u001b[1;32m-> 1110\u001b[1;33m             \u001b[1;32mreturn\u001b[0m \u001b[0mforward_call\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0minput\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m   1111\u001b[0m         \u001b[1;31m# Do not call functions when jit is used\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1112\u001b[0m         \u001b[0mfull_backward_hooks\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mnon_full_backward_hooks\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\Temp;\\ipykernel_11144\\2568048666.py\u001b[0m in \u001b[0;36mforward\u001b[1;34m(self, x, edge_index)\u001b[0m\n\u001b[0;32m     14\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     15\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0mforward\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0medge_index\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 16\u001b[1;33m         \u001b[0mh\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mconv1\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0medge_index\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     17\u001b[0m         \u001b[0mh\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mh\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtanh\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     18\u001b[0m         \u001b[0mh\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mconv2\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mh\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0medge_index\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\anaconda3\\envs\\graph\\lib\\site-packages\\torch\\nn\\modules\\module.py\u001b[0m in \u001b[0;36m_call_impl\u001b[1;34m(self, *input, **kwargs)\u001b[0m\n\u001b[0;32m   1108\u001b[0m         if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks\n\u001b[0;32m   1109\u001b[0m                 or _global_forward_hooks or _global_forward_pre_hooks):\n\u001b[1;32m-> 1110\u001b[1;33m             \u001b[1;32mreturn\u001b[0m \u001b[0mforward_call\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0minput\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m   1111\u001b[0m         \u001b[1;31m# Do not call functions when jit is used\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1112\u001b[0m         \u001b[0mfull_backward_hooks\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mnon_full_backward_hooks\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\anaconda3\\envs\\graph\\lib\\site-packages\\torch_geometric\\nn\\conv\\gcn_conv.py\u001b[0m in \u001b[0;36mforward\u001b[1;34m(self, x, edge_index, edge_weight)\u001b[0m\n\u001b[0;32m    189\u001b[0m                     \u001b[0medge_index\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mcache\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    190\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 191\u001b[1;33m         \u001b[0mx\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mlin\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    192\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    193\u001b[0m         \u001b[1;31m# propagate_type: (x: Tensor, edge_weight: OptTensor)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\anaconda3\\envs\\graph\\lib\\site-packages\\torch\\nn\\modules\\module.py\u001b[0m in \u001b[0;36m_call_impl\u001b[1;34m(self, *input, **kwargs)\u001b[0m\n\u001b[0;32m   1108\u001b[0m         if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks\n\u001b[0;32m   1109\u001b[0m                 or _global_forward_hooks or _global_forward_pre_hooks):\n\u001b[1;32m-> 1110\u001b[1;33m             \u001b[1;32mreturn\u001b[0m \u001b[0mforward_call\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0minput\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m   1111\u001b[0m         \u001b[1;31m# Do not call functions when jit is used\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1112\u001b[0m         \u001b[0mfull_backward_hooks\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mnon_full_backward_hooks\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\anaconda3\\envs\\graph\\lib\\site-packages\\torch_geometric\\nn\\dense\\linear.py\u001b[0m in \u001b[0;36mforward\u001b[1;34m(self, x)\u001b[0m\n\u001b[0;32m    116\u001b[0m             \u001b[0mx\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mTensor\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m \u001b[0mThe\u001b[0m \u001b[0mfeatures\u001b[0m\u001b[1;33m.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    117\u001b[0m         \"\"\"\n\u001b[1;32m--> 118\u001b[1;33m         \u001b[1;32mreturn\u001b[0m \u001b[0mF\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mlinear\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mweight\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mbias\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    119\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    120\u001b[0m     \u001b[1;33m@\u001b[0m\u001b[0mtorch\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mno_grad\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mRuntimeError\u001b[0m: mat1 and mat2 shapes cannot be multiplied (34x34 and 21x4)"
     ]
    }
   ],
   "source": [
    "import time\n",
    "\n",
    "model = GCN()\n",
    "criterion = torch.nn.CrossEntropyLoss()  # Define loss criterion.\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=0.01)  # Define optimizer.\n",
    "\n",
    "def train(data):\n",
    "    optimizer.zero_grad()  # Clear gradients.\n",
    "    out, h = model(data.x, data.edge_index)  # Perform a single forward pass.\n",
    "    loss = criterion(out[data.train_mask], data.y[data.train_mask])  # Compute the loss solely based on the training nodes.\n",
    "    loss.backward()  # Derive gradients.\n",
    "    optimizer.step()  # Update parameters based on gradients.\n",
    "    return loss, h\n",
    "\n",
    "for epoch in range(401):\n",
    "    loss, h = train(data)\n",
    "    # Visualize the node embeddings every 10 epochs\n",
    "    # if epoch % 10 == 0:\n",
    "    #     visualize(h, color=data.y, epoch=epoch, loss=loss)\n",
    "    #     time.sleep(0.3)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "d118e8625ddf9565a7a601079b239702bff57fbe54bd264ef0a63b2e5e00a7ee"
  },
  "kernelspec": {
   "display_name": "Python 3.7.11 ('graph')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.11"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
